package simulation;

/* To do:
	- Create Car Arrivals on every x
	- Create Car Accidents if needed every x
	- Digest Events clean up
*/


// Imported Files
import events.*;
import gui.*;
import Images.*;
import neighborhood.Neighborhood;
import road.Road;
import statistics.*;
import train.Train;
import traveler.*;
import priorityQueue.PriorityQueue;
import java.util.*;

/**
 * Simulation is the core driver of the Traffic Simulation. Two steps need to be
 * taken to correctly setup a Simulation. First the Simulation constructor must
 * be called. This will define allow for all of the subclasses to be defined.
 * Then the SetInitialConditions must be called. This will allow for all of the
 * initial events in the system to be defined and timed so that they can be
 * processed.
 * 
 * @author Team Switchboard Boxcar
 * 
 */
public class Simulation
{
	private Neighborhood myNeighborhood[];
	private Statistics myStatistics;
	private double AccidentProbability = 0.10; 
	private PriorityQueue myPQueue;
	private int CurrentTime;
	private int LengthOfSimulation;
	
	
	// Finished
	/**
	 * Simulation() is the basic constructor for the simulation class. This
	 * constructor defines all of the base operations in addition to defining
	 * the four {@link Neighborhood}s that are a part of the Simulation. It can
	 * always be assumed that Neighborhood 0 = UC, Neighborhood 1 = I,
	 * Neighborhood 2 = DT and Neighborhood 3 = LC. When a 4x5 array is called
	 * it can be assumed that [x][y] represents Neighborhood x, {@link Road} y.
	 * If Road y does not exist in a specific neighboorhood then a value of 0
	 * should be input into all arrays where necessary.
	 * 
	 * @param SimLength
	 *            This is an integer representing the length of the simulation.
	 *            This should be a measurement of the total time in minutes.
	 * @param NumRds
	 *            The number of roads in each of the neighborhoods that will be
	 *            defined. This array should contain 4 values between 3 and 5 in
	 *            size.
	 * @param NeighMaxSpd
	 *            This is a 4x5 array which contains the maximum speed for Roads
	 *            0 - 4 in Neighborhoods 0-3.
	 * @param NeighLaneSize
	 *            This is a 4x5 array which represents the lane size for Roads 0 -
	 *            4 in Neighborhoods 0 - 3.
	 * @param NeighRdLength
	 *            This is a 4x5 array which represents road lengths for Roads 0 -
	 *            4 in Neighborhoods 0 - 3.
	 * @param NeighLaneMult
	 *            This is a 4x5 array which represents road lane multipliers
	 *            (doubles) for Roads 0 -4 in Neighborhoods 0 - 3.
	 * @param NeighEndNeigh
	 *            This is a 4x5 array which represents the ending Neighborhood
	 *            for Roads 0 - 4 in Neighborhhods 0 - 3
	 */
	public Simulation(int SimLength, int NumRds[], int NeighMaxSpd[][],
			int NeighLaneSize[][], int NeighRdLength[][],
			double NeighLaneMult[][], int NeighEndNeigh[][]) {
		myStatistics = new Statistics();
		myPQueue = new PriorityQueue();
		LengthOfSimulation = SimLength;
		CurrentTime = 0;
		
		// Neigh 0 = UC
		// Neigh 1 = I
		// Neigh 2 = DT
		// Neigh 3 = LC
		myNeighborhood = new Neighborhood[4];
		
		// Building Neighborhoods 0 - 4
		for (int counter = 0; counter < 4; counter++) {
			myNeighborhood[counter] = new Neighborhood(NeighMaxSpd[counter],
					NeighLaneSize[counter], NeighRdLength[counter],
					NeighLaneMult[counter], NeighEndNeigh[counter], counter);
		}
		
		
	}
	
	
	// Finished
	/**
	 * SetInitialConditions is called to created the base set of events that
	 * could possibly be included in every simulation. These include
	 * {@link Rain}, travelers and {@link Train_Arrival}s.
	 * 
	 * @param rainProb
	 *            A double value representing the probability of rain should be
	 *            input. The three expected values should be 1, 0.15 or 0. If 1
	 *            is input, the user wants rain to occur. If 0.15 is input, the
	 *            user wants rain to randomly occur. If 0 is input, the user
	 *            does desire for rain to occur.
	 * @param trafficLevel
	 *            TrafficLevel represents the volume of traffic occuring in this
	 *            simulation. The three expected values are 0.30 (Light), 0.60
	 *            (Medium), or 0.85 (Heavy).
	 * @param numTrains
	 *            NumTrains represents the total quantity of trains that will be
	 *            generated to carry travelers in this simulation.
	 * @param numAccidents
	 *            The total number of accidents that are being processed by in
	 *            this simulation.
	 * @param StartingNeigh
	 *            An array of the starting neighborhoods of length equal to
	 *            numAccidents.
	 * @param RoadIndx
	 *            An array of the road indexes where the accident occurs of
	 *            length equal to numAccidents.
	 * @param accidentTime
	 *            An array of the time the accident occurs at of length equal to
	 *            numAccidents.
	 */
	public void SetInitialConditions(double rainProb, double trafficLevel, int numTrains,
										int numAccidents, int StartingNeigh[], int RoadIndx[],
										int accidentTime[])
	{
		// See if rain is generated?  15% if not user specified
		double random = Math.random();
		if(rainProb >= random)
			GenerateRain(0.15);
		
		// Create AddTraveler Events
		GenerateAddTravEvents((int)(trafficLevel*400000));
		
		// Generate Trains
		// Generate Train Events
		// Place Train Events
		GenerateTrains(numTrains);
		
		// Create Car Accidents
		GenerateCarAccidents(	numAccidents, StartingNeigh, 
								RoadIndx, accidentTime);
	}
	
		
	// Finished
	/**
	 * GenerateRain is the function that is called to generate a {@link Rain}
	 * {@link Event} in the simulation.
	 * 
	 * @param RainAccidentProb
	 *            is the accident probability placed into the Rain Event. When
	 *            this event is processed, the simulation will update the
	 *            probability of accidents in the simulaton.
	 */
	public void GenerateRain(double RainAccidentProb)
	{
		PlaceEvent(CreateEvent(	4, -1, -1, -1, false, 
								null, -1, -1, RainAccidentProb, null));
	}
	
	
	// Finished
	/**
	 * GenerateAddTravEvents is a function that is called during the system
	 * initialization to create events at even intervals to properly distribute
	 * travelers coming into the system.
	 * 
	 * @param totalPopulation
	 *            Expects an integer representing the total number of travelers
	 *            and cars that need to be accounted for during the simulation.
	 */
	public void GenerateAddTravEvents(int totalPopulation)
	{
		int PopulationIntervals = 10; 	// Total Insertions of Travelers/Cars
		int IntervalOfInsertion = 30; 	// Time Between Insertions of
										// Travelers/Cars in Minutes
		int InitialTime = 0; 			// First Insertion of Travelers
		int PopPerInterval = totalPopulation / PopulationIntervals; 
										// Trav. Inserted Per Interval
		int TotalTravInserted = 0;

		// Creates # of AddTrav Events = PopulationIntervals
		for (int i = PopulationIntervals; i > 0; i--) 
		{
			// Creates AddTrav Event w/ PopPerInterval people @ time intervals 
			// spaced approximately IntervalOfInsertion Apart
			if (i > 1)	
			{
				PlaceEvent(CreateEvent(3, InitialTime, -1, -1, false, null, -1,
						PopPerInterval, -1, null));
				InitialTime += IntervalOfInsertion;
				TotalTravInserted += PopPerInterval;
			} 
			// Makes sure that all Travelers have been accounted for in the 
			// creation of AddTrav Events.
			else  
			{
				PlaceEvent(CreateEvent(3, InitialTime, -1, -1, false, null, -1,
						totalPopulation - TotalTravInserted, -1, null));
			}
		}
	}
		
	
	// Finished (I think)
	/**
	 * GenerateTrains is a freaking complicated Function. Steps are as follows:
	 * <p>
	 * 1. Create an array called trainArray[x][y] where (x,0) represents rail x - >
	 * x+1 and (x,1) represents rail x+1 -> x. This function is also used to track
	 * the final rail that receives one extra train.
	 * <p>
	 * 2. Build an array representing the distance between any two trains on a
	 * specific track. This array follows the same [x][0,1] format as step 1.
	 * <p>
	 * 3. Build an array representing the time it takes to travel the distance
	 * between any two trains on a specific track. This array follows the same
	 * [x][0,1] format as step 1.
	 * <p>
	 * 4. Loop through all tracks which received the higher amount of trains. Trains
	 * are created, placed into an event and then that event is placed into the
	 * priority queue.
	 * <p>
	 * 4a. If a a CNTRCLKWS track was the first track not to receive the extra train
	 * it is dealt with seperately.
	 * <p>
	 * 5. All tracks that received the lower amount of trains are then addressed.
	 * Trains are created, placed into an event and then placed into the priority
	 * queue.
	 * 
	 * @param numTrains
	 *            The number of trains that need to be generated at the beginning of
	 *            the simulation.
	 */
	public void GenerateTrains(int numTrains)
	{
		// [x][0] Starting Neighborhood = x, assume tracks run CLKWS
		// [x][1] Ending Neighborhood = x, assume tracks run CNTRCLKWS
		int trainArray[][] = new int[4][2];  
		int trainsRemaining = numTrains;
		int trainCounter = -1;
		int stopPosition[] = {0,0};
		// Speed of Train in Miles per Minute
		double trainSpeed = 80/(double)60;	

		// Fill Array with trains starting at the railroad tracks between UC and I
		// Loops until all trains have been placed
		while(trainsRemaining != 0)
		{
			trainCounter = (trainCounter + 1)%4;
			if(trainsRemaining!= 0)
			{
				trainArray[trainCounter][0]++;
				trainsRemaining--;
				stopPosition[0] = trainCounter;
				stopPosition[1] = 0;
			}
			if(trainsRemaining!= 0)
			{
				trainArray[trainCounter][1]++;
				trainsRemaining--;
				stopPosition[1] = 1;
			}
		}
		
		// Build Array of Distance between Trains
		// Distance between trains starting (10 miles / # trains)
		// timeForTrain[x][y] : y = 0 - Start Neigh = x, End = x+1
		//						y = 1 - Start Neigh = x+1, End = x
		// UC <-> I, LC <-> DT
		double milesBetweenTrains[][] = new double[4][2];
		int counter = 0;
		for(counter = 0; counter < 2; counter++)
		{
			if (trainArray[counter*2][0] != 0)
				milesBetweenTrains[counter*2][0] = 10/trainArray[counter*2][0];
			if (trainArray[counter*2][1] != 0)
				milesBetweenTrains[counter*2][1] = 10/trainArray[counter*2][1];
		}
		
		// Distance between trains starting (10 miles / # trains)
		// DT <-> I, LC <-> UC
		for(counter = 0; counter < 2; counter++)
		{
			if (trainArray[counter*2+1][0] != 0)
				milesBetweenTrains[counter*2+1][0] = 20/trainArray[counter*2+1][0];
			if (trainArray[counter*2+1][1] != 0)
				milesBetweenTrains[counter*2+1][1] = 20/trainArray[counter*2+1][1];
			
		}
		
		
		
		// Build Array of time it takes for a Train to reach the next Starting Position
		// Distance Between Train (Miles) / Train Speed(Miles per Minute)
		// timeForTrain[x][y] : y = 0 - Start Neigh = x, End = x+1
		//						y = 1 - Start Neigh = x+1, End = x
		int timeForTrain[][] = new int[4][2];
		for(counter = 0; counter < 4; counter++)
		{
			timeForTrain[counter][0] = (int)(Math.ceil(milesBetweenTrains[counter][0]/trainSpeed));
			timeForTrain[counter][1] = (int)(Math.ceil(milesBetweenTrains[counter][1]/trainSpeed));
		}
		

		// arrival time [x]= Time for next train to reach the station,  x = Start Neigh 
		int arrivalTime_CLKWS[] = {0,0,0,0};
		// arrival time [x]= Time for next train to reach the station,  x = Dest Neigh		
		int arrivalTime_CCLKWS[] = {0,0,0,0};
		int neighCount;

		// Locations x,y =< stopPosition[x],stopPosition[y] have 1 more train than
		// Locations x,y, > stopPosition[x],stopPosition[y]
		// Counter loops until all Trains have been placed
		for(counter = 0; counter < trainArray[0][0]; counter++)
		{
			// Neighborhood train theoretically started at
			for(neighCount = 0; neighCount <= stopPosition[0]; neighCount++)
			{
				// Creating Trains Going CLKWS
				PlaceEvent(
						CreateEvent(5,arrivalTime_CLKWS[neighCount],
								-1,-1,false,null,(neighCount+1)%4 /*dest*/,
								-1, -1,	myNeighborhood[neighCount].
								CreateTrain(0-arrivalTime_CLKWS[neighCount], 
											100/*MaxPass*/, 1/*CLKWS*/)));
				// Increment Arrival time
				arrivalTime_CLKWS[neighCount] += timeForTrain[neighCount][0];
		
				// Creating Trains Going CCLKWS
				// Need to check to see if final CCLKWS did not receive a train
				if(!(neighCount == stopPosition[0] && 
						stopPosition[1] == 0))
						{
					// Create Train, Event then Place
							PlaceEvent(
							CreateEvent(5,arrivalTime_CCLKWS[(neighCount+1)%4],
									-1,-1,false,null,neighCount,-1, -1,	
									myNeighborhood[(neighCount+1)%4].
									CreateTrain(0-arrivalTime_CCLKWS[neighCount], 
												100, -1)));
					// Increment Arrival time
					arrivalTime_CCLKWS[neighCount] += timeForTrain[neighCount][1];
			
						}
			}
		}
		
		// Need check for CCLKWS at stopPosition[0], stopPosition[1] = 1
		if(	stopPosition[1] == 0)
		{
			for(counter = 0; counter < trainArray[stopPosition[0]][1]; counter++)
			{
				// Create Train, Event then Place
				PlaceEvent(
						CreateEvent(5,arrivalTime_CCLKWS[(stopPosition[0]+1)%4],
								-1,-1,false,null,stopPosition[0],-1, -1,	
								myNeighborhood[(stopPosition[0]+1)%4].
								CreateTrain(0-arrivalTime_CCLKWS[stopPosition[0]], 
											100, -1)));
				// Increment Arrival Time
				arrivalTime_CCLKWS[stopPosition[0]] += timeForTrain[stopPosition[0]][1];
			}
		}
		
		
		if(trainArray[0][0]> trainArray[3][1])
		{
			for(counter = 0; counter < trainArray[3][1]; counter++)
			{
				// Neighborhood train theoretically started at
				for(neighCount = stopPosition[0]+1; neighCount < 4; neighCount++)
				{
					// Creating Trains Going CLKWS
					PlaceEvent(
							CreateEvent(5,arrivalTime_CLKWS[neighCount],
									-1,-1,false,null,(neighCount+1)%4 /*dest*/,
									-1, -1,	myNeighborhood[neighCount].
									CreateTrain(0-arrivalTime_CLKWS[neighCount], 
												100/*MaxPass*/, 1/*CLKWS*/)));
					arrivalTime_CLKWS[neighCount] += timeForTrain[neighCount][0];
			
					// Creating Trains Going CCLKWS
					if(!(neighCount == stopPosition[0] && 
							stopPosition[1] == 0))
							{
						PlaceEvent(
								CreateEvent(5,arrivalTime_CCLKWS[(neighCount+1)%4],
										-1,-1,false,null,neighCount /*dest*/,
										-1, -1,	myNeighborhood[(neighCount+1)%4].
										CreateTrain(0-arrivalTime_CCLKWS[neighCount], 
													100/*MaxPass*/, -1/*CCLKWS*/)));
						arrivalTime_CCLKWS[neighCount] += timeForTrain[neighCount][1];
				
							}
				}
			}
		}
	}
		
	
	// Finished
	/**
	 * GenerateCarAccidents builds and places accidents into the
	 * {@link priorityQueue}. The arrays passed into this function should all
	 * be of the length specified by the value of numAccidents. This accidents
	 * are all the accidents that are created by the user of the simulation.
	 * These accidents are not randomly generated by the simulation itself.
	 * 
	 * @param numAccidents
	 *            The total number of accidents that are being processed by in
	 *            this simulation.
	 * @param StartingNeigh
	 *            An array of the starting neighborhoods of length equal to
	 *            numAccidents.
	 * @param RoadIndx
	 *            An array of the road indexes where the accident occurs of
	 *            length equal to numAccidents.
	 * @param accidentTime
	 *            An array of the time the accident occurs at of length equal to
	 *            numAccidents.
	 */
	public void GenerateCarAccidents(int numAccidents, int StartingNeigh[], 
			int RoadIndx[], int accidentTime[])
	{
		for (int counter = 0; counter < numAccidents; counter++) {
			PlaceEvent(CreateEvent(1, accidentTime[counter],
					StartingNeigh[counter], RoadIndx[counter], true, null, -1, -1, -1, null));
		}
	}
		
	
	// Finished
	/**
	 * AddTravelers is the function called to add a specified number of
	 * {@link Traveler}s and {@link Car}s to the simulation. The input value
	 * is first seperated into the four different {@link Neighborhood}s. Then
	 * the total number of people waiting at each neighborhood is split between
	 * those that are driving {@link Car}s and those that are taking the
	 * {@link Train}s. Once this is compelted, the travelers are passed to the
	 * specific neighborhood where they are created and added to the waitnig
	 * queues for their method of travel.
	 * 
	 * @param travToAdd
	 *            An integer representing the total number of people that are
	 *            being introduced into the simulation.
	 */
	public void AddTravelers(int travToAdd)
	{
		// Generate Total Population in Each Neigh
		int pop[] = new int[4];
		pop[0] = (int) (0.25 * travToAdd);				// UC
		pop[1] = (int) (0.10 * travToAdd);				// I
		pop[2] = (int) (0.10 * travToAdd); 				// DT
		pop[3] = travToAdd - pop[0] - pop[1] - pop[2];	// LC

		// Seperate Cars in Each Neigh
		// Array Location [x][y]
		// x = Starting Neighborhood
		// y = Destination Neighborhood
		// if (x = y) = Total Starting Population
		int car[][] = new int[4][4];

		// UpperClass
		car[0][0] = (int) (0.92 * pop[0]);
		car[0][1] = (int) (0.20 * car[0][0]);
		car[0][2] = (int) (0.75 * car[0][0]);
		car[0][3] = car[0][0] - car[0][1] - car[0][2]; // 5%

		// Industrial
		car[1][1] = (int) (0.60 * pop[1]);
		car[1][0] = (int) (0.10 * car[1][1]);
		car[1][2] = (int) (0.75 * car[1][1]);
		car[1][3] = car[1][1] - car[1][0] - car[1][2]; // 15%

		// Downtown
		car[2][2] = (int) (0.75 * pop[2]);
		car[2][0] = (int) (0.25 * car[2][2]);
		car[2][1] = (int) (0.50 * car[2][2]);
		car[2][3] = car[2][2] - car[2][0] - car[2][1]; // 25%

		// Lower Class
		car[3][3] = (int) (0.75 * pop[3]);
		car[3][0] = (int) (0.05 * car[2][2]);
		car[3][1] = (int) (0.30 * car[2][2]);
		car[3][2] = car[3][3] - car[3][0] - car[3][1]; // 65%

		// Generate Train Travelers in Each Neighborhood
		// Array Location [x][y]
		// x = Starting Neighborhood
		// y = Destination Neighborhood
		// if (x = y) = Total Starting Population
		int train[][] = new int[4][4];
		for (int i=0; i < 4; i++) {
			train[i][i] = pop[i] - car[i][i];
		}

		// UpperClass
		train[0][1] = (int) (0.20 * train[0][0]);
		train[0][2] = (int) (0.75 * train[0][0]);
		train[0][3] = train[0][0] - train[0][1] - train[0][2]; // 5%

		// Industrial
		train[1][0] = (int) (0.10 * train[1][1]);
		train[1][2] = (int) (0.75 * train[1][1]);
		train[1][3] = train[1][1] - train[1][0] - train[1][2]; // 15%

		// Downtown
		train[2][0] = (int) (0.25 * train[2][2]);
		train[2][1] = (int) (0.50 * train[2][2]);
		train[2][3] = train[2][2] - train[2][0] - train[2][1]; // 25%

		// Lower Class
		train[3][0] = (int) (0.05 * train[2][2]);
		train[3][1] = (int) (0.30 * train[2][2]);
		train[3][2] = train[3][3] - train[3][0] - train[3][1]; // 65%

		// Place Cars & Travelers into their Queues
		boolean destFound = false;
		for (int j=0; j < 4; j++)		// j = Starting Neighborhood
		{
			for (int k=0; k < 4; k++)	// k = Destination Neighborhood
			{
				destFound = false;
				if (j != k) 
				{
					while (train[j][k] > 0) 
					{	
						myNeighborhood[j].CreateTraveler(k, CurrentTime);	
						train[j][k]--;
					}
					
						for(int l = 0; l < 5; l ++)
						{
							// Direct Route Found
							if(myNeighborhood[j].GetRoadEndNeigh(l) == k && destFound == false)
							{
								destFound = true;
								while(car[j][k] > 0)
								{
								myNeighborhood[j].CreateCar(l, k, CurrentTime);
								car[j][k]--;
								}
							}
						}
						
						// Direct Route Not Found
						if(destFound = false)
						{
							// FInd some road that works
							for(int m = 0; m < 5; m++)
							{
								if(myNeighborhood[j].roadExists(m) && destFound == false)
								{
									destFound = true;
									while(car[j][k] > 0)
									{
									myNeighborhood[j].CreateCar(m, k, CurrentTime);
									car[j][k]--;
									}
								}
							}
						}
					
				}
			}
		}
	}

	
	// Finished
	/**
	 * ProcessEvents is the core driver for the Simulation. This function will
	 * process every minute in the simulation. There are three specific parts to
	 * this function.
	 * <p>
	 * 1.) At the start of every hour, any {@link Car_Accident}s needed are created.
	 * <p>
	 * 2.) Every minute more cars are placed into {@link Car_Arrival} Events
	 * from the {@link Road} Waiting queues.
	 * <p>
	 * 3.) If any events on the {@link PriorityQueue} occur at the current
	 * minute, they are processed.
	 */
	public void ProcessEvents()
	{
		// Check to see if PQueue needs to do something.
		while(myPQueue.peekTop() < 0)
		{	DigestEvent(GetNextEvent());	}
		
		// Process the Simulation until end of user specified length
		while(CurrentTime != LengthOfSimulation)
		{
			// If it is the start of the hour, check to create Car Accidents
			if(CurrentTime%60 == 59)
			{	CreateCarAccidents();	}
			
			// Remove Cars from Waiting Queues and place them into PQueue
			ProcessDepartures();
			
			// Check to see if PQueue needs to do something.
			while(CurrentTime >= myPQueue.peekTop())
			{	
				if (CurrentTime == 117)//DTG
				{
				int foo = 13;
				foo = 4;
				}
				DigestEvent(GetNextEvent());	}
			
			// Advance the CurrentTime of the Simulation
			CurrentTime++;
			

		}
	}
	
	
	// Finished
	// Finished
	/**
	 * GetNextEvent is the function called to return the {@link Event} at the
	 * top of the {@link PriorityQueue}.
	 * 
	 * @return Returns the {@link Event} at the top of the Priority Queue.
	 */
	public Event GetNextEvent()
	{	return	myPQueue.dequeue();	}
	
	
	// Finished
	/**
	 * CreateCarAccidents is a function that is called before the start of every
	 * hour to determine if any car accidents occur on any roads during the
	 * specified hour. It will check every {@link Road} in each {@link Neighborhood}. There is a
	 * nominal 10% chance a {@link Car_Accident} will occur. For the route between the UC
	 * and DT there is a 20% chance an accident will occur. Both of these
	 * percentages will increase if {@link Rain} is occuring in the simulation.
	 * 
	 */
	public void CreateCarAccidents()
	{
		// Probability an AccidentOccurs
		Random randomDouble = new Random();
		
		// Define accidents array
		for (int neighCounter = 0; neighCounter < 4; neighCounter++) {
			for (int roadCounter = 0; roadCounter < 5; roadCounter++) {
				// UC to DT has seperate Probability
				if (roadCounter == 3
						&& (neighCounter == 0 || neighCounter == 2)) {
					// Check to see if accident occurs
					if (randomDouble.nextDouble() < 2 * AccidentProbability)
						//	Check to see if road exists
						if (myNeighborhood[neighCounter]
								.roadExists(roadCounter)) 	
							PlaceEvent(CreateEvent(1, CurrentTime+1,
									neighCounter, roadCounter, true, null, 0,
									0, 0, null));
				} else {
					// Check to see if neighborhood exists
					if (randomDouble.nextDouble() < 1 * AccidentProbability)
						// Check to see if road exists
						if (myNeighborhood[neighCounter]
								.roadExists(roadCounter))
							PlaceEvent(CreateEvent(1, CurrentTime+1,
									neighCounter, roadCounter, true, null, 0,
									0, 0, null));
				}
			}
		}
	}
	
	// Finished
	// Finished
	/**
	 * PlaceEvent is a function that is used to take an {@link Event} created by
	 * the Simulation and place it into the {@Link PriorityQueue}. This event
	 * can then be processed later in the simulation.
	 * 
	 * @param CreatedEvent
	 *            PlaceEvent expects an input Event of any kind that needs to be
	 *            placed into the PriorityQueue for future processing.
	 */
	public void PlaceEvent(Event CreatedEvent)
	{	myPQueue.enqueue(CreatedEvent); }
	
	
	// Finished
	/**
	 * ProcessDepartures is called to move {@link Car}s from the
	 * {@link Neighborhood} they are waiting in into {@link Car_Arrival}
	 * {@link Event}s. This function should be called every minute to simulate
	 * the constant flow of traffic.
	 * 
	 */
	public void ProcessDepartures()
	{
		// Get Cars Per Neighborhood / Road
		// Pull Car per Neighborhood / Road
		// Generate Car Arrival per Car / Road
		int CarsToPull = 0;
		
		for (int neighCounter = 0; neighCounter < 4; neighCounter++) 
		{
			for (int roadCounter = 0; roadCounter < 5; roadCounter++) 
			{
				if(myNeighborhood[neighCounter].roadExists(roadCounter))
				{
					CarsToPull = myNeighborhood[neighCounter].GetCarsPerMin(roadCounter);
					while(CarsToPull != 0)
					{
						if(!(myNeighborhood[neighCounter].ReturnNeighQueue(roadCounter).isEmpty() && 
								myNeighborhood[neighCounter].ReturnRoadQueue(roadCounter).isEmpty())) // ADDED
						{
							PlaceEvent(CreateEvent(2,CurrentTime + myNeighborhood[neighCounter].GetTravelTime(roadCounter), neighCounter,roadCounter,false,myNeighborhood[neighCounter].MoveCarToRoad(roadCounter),myNeighborhood[neighCounter].GetRoadEndNeigh(roadCounter),0,0,null));
						}
						CarsToPull--;
					}
				}
			}
		}
		
	}
			
	
	/**
	 * DigestEvent is the function that processes {@link Event}s that are
	 * removed from the {@link PriorityQueue}. It determines what type of Event
	 * is being processed then it performs the necessary actions.
	 * 
	 * @param DigestEvent An Event to be processed in the Simulation.
	 */
	public void DigestEvent(Event DigestEvent )
	{
		
		// Car_Accident
		if (DigestEvent.GetEffect() == 1) {
			// Car Accident turns On
			if (((Car_Accident) (DigestEvent)).IsAccident()) {
				// Speed slowed down to 50%
				myNeighborhood[((Car_Accident) (DigestEvent)).GetNeighborhood()]
						.UpdateRoadSpeed(((Car_Accident) (DigestEvent))
								.GetRoad(), 0.50);

				// Create new event to turn accident off
				// Place event into pQueue 1 hour from now (time = now + 30)
				PlaceEvent(CreateEvent(1, CurrentTime + 30,
						((Car_Accident) (DigestEvent)).GetNeighborhood(),
						((Car_Accident) (DigestEvent)).GetRoad(), false, null,
						-1, -1, -1, null));
				
				SetStatistics(DigestEvent);
			}

			// Car Accident Turns Off
			if (!((Car_Accident) (DigestEvent)).IsAccident()) {
				// Reset Road Speed
				myNeighborhood[((Car_Accident) (DigestEvent)).GetNeighborhood()]
						.UpdateRoadSpeed(((Car_Accident) (DigestEvent))
								.GetRoad(), 2.00);
			}
		}
		
		// Car_Arrival
		if (DigestEvent.GetEffect() == 2) 
		{
			// Always Need to Process Event
			SetStatistics(DigestEvent);
			
			// Check to see if Car is not at destination, if so move it to the
			// waiting queue at that neighborhood
			if (!(((Car_Arrival) (DigestEvent)).GetDestination() 
					== ((Car_Arrival) (DigestEvent)).GetCar().GetDestination())) {
				myNeighborhood[((Car_Arrival) (DigestEvent)).GetDestination()]
						.MoveCarToWait(((Car_Arrival) (DigestEvent)).GetCar());
			}

		}
		
		// Generate_Travelers
		if (DigestEvent.GetEffect() == 3) 
		{	AddTravelers(((Generate_Travelers)(DigestEvent)).AddTravelers());	}
		
		// Rain
		if (DigestEvent.GetEffect() == 4) 
		{
			for (int n = 0; n < myNeighborhood.length; n++) {
				for (int r = 0; r < 5; r++) {
					myNeighborhood[n].UpdateRoadSpeed(r, 0.85);
				}
			}	
			AccidentProbability = ((Rain)(DigestEvent)).GetProb();
			SetStatistics(DigestEvent);
		}
		
		// Train_Arrival
		if (DigestEvent.GetEffect() == 5) 
		{
			// Send to Set Statistics
			SetStatistics(DigestEvent);
			// Unload/Load Passengers
			myNeighborhood[((Train_Arrival) (DigestEvent)).GetDestination()]
					.TransferTrainPass(((Train_Arrival) (DigestEvent)).GetTrain());
			// Update Train Depart Time
			((Train_Arrival)(DigestEvent)).GetTrain().SetDepartTime(CurrentTime + 1);
			int endNeigh = -1;
			int travelTime = 8;
			if(((Train_Arrival)(DigestEvent)).GetTrain().IsTravelingCW())
				endNeigh = Neighborhood.Next(((Train_Arrival) (DigestEvent)).GetDestination());
			else
				endNeigh = Neighborhood.Prev(((Train_Arrival) (DigestEvent)).GetDestination());
			if ((((Train_Arrival) (DigestEvent)).GetTrain().IsTravelingCW() && 
							(endNeigh == 2 || endNeigh == 0))
					|| (!(((Train_Arrival) (DigestEvent)).GetTrain().IsTravelingCW()) && 
								(endNeigh == 1 || endNeigh == 3))) {
				travelTime = 15;
			}
			PlaceEvent(CreateEvent(5,travelTime+CurrentTime,-1,-1,false,null,endNeigh, -1, 0,((Train_Arrival)(DigestEvent)).GetTrain()));
			// Do what with these travelers now?
		}
	}
	
	
	// Finished
	/**
	 * CreateEvent is a basic constructor that can create {@link Event}s of any
	 * type. When using this function any input values that are not necessary
	 * should be input as null(Car/Train), -1(int/double) or false (boolean).
	 * 
	 * @param EventType
	 *            The type of event that is being created. This value should be
	 *            an integer from 1 - 5. Any outside values will fail to create
	 *            an event. [All Events]
	 * @param Time
	 *            The time that the event occurs. This value is used by the
	 *            {@link PriorityQueue} to detemrine the priority of the event.
	 *            [All Events]
	 * @param StartNeigh
	 *            The starting neighborhood index for any event involving the
	 *            road operations. [{@link Car_Accident} and
	 *            {@link Car_Arrival} only, -1 for other event types]
	 * @param RoadIndex
	 *            The road index for a car events. [{@link Car_Accident} and
	 *            {@link Car_Arrival} only, -1 for other event types]
	 * @param TypeAccident
	 *            A boolean value representing whether an accident occurs. [{@link Car_Accident}
	 *            only, false for other event types]
	 * @param MyCar
	 *            The {@link Car} that is being processed by a
	 *            {@link Car_Arrival} Event. [{@Car_Arrival} only, null for
	 *            other event types]
	 * @param EndNeigh
	 *            The ending neighborhood for any event regarding vehicles
	 *            arriving at their destination. [{@link Car_Arrival} and
	 *            {@link Train_Arrival} only, -1 for other event types]
	 * @param numTrav
	 *            The number of travelers that need to be generated in an event. [{@link Generate_Travelers}
	 *            only, -1 for all other event types]
	 * @param AccidentProb
	 *            The percent probability that an accident will occur if rain is
	 *            simulated. [{@link Rain} only, -1 for all other event types]
	 * @param MyTrain
	 *            The {@link Train} that is being processed by a
	 *            {@link Train_Arrival} event. [{@link Train_Arrival} only,
	 *            null for all other event types]
	 * @return Returns the event that is created in this function which can then
	 *         be placed into the Priority Queue.
	 */
	public Event CreateEvent(	int EventType, int Time, int StartNeigh, int RoadIndex,
								boolean TypeAccident, Car MyCar, int EndNeigh, int numTrav,
								double AccidentProb, Train MyTrain)
	{
		// Car_Accident
		if (EventType == 1) 
		{	
			Car_Accident createAccident = 
				new Car_Accident( StartNeigh, RoadIndex, Time, TypeAccident);
			return createAccident;
		}
		
		// Car_Arrival
		if (EventType == 2) 
		{
			Car_Arrival carArrives = 
				new Car_Arrival( MyCar, EndNeigh, StartNeigh, RoadIndex, Time);
			return carArrives;
		}
		
		// Generate_Travelers
		if (EventType == 3) 
		{
			Generate_Travelers genTrav = 
				new Generate_Travelers(numTrav, Time);
			return genTrav;
		}
		
		// Rain
		if (EventType == 4) 
		{
			Rain makeRain = 
				new Rain(AccidentProb);
			return makeRain;
		}
		// Train_Arrival
		if (EventType == 5) 
		{
			Train_Arrival trainArrives = 
				new Train_Arrival(MyTrain, EndNeigh, Time);
			return trainArrives;
		}
		else return null;
	}

	
	
	/**
	 * SetStatistics updates the stats for average speed (road/track)
	 * total cars, accidents, and rain.
	 * 
	 * @param MyEvent 
	 * 			The event that is being processed.  If MyEvent is a valid event,
	 * 			the statistics are updated, if it is not, SetStatistics does nothing. 
	 */
	public void SetStatistics(Event MyEvent)
	{
		int TravelingTime = 0;//minutes
		int TempNeighIndex;
		int TempRouteIndex;
		
		if (MyEvent.GetEffect() == 1)//Car_Accident
		{
			Car_Accident TempAccident = ((Car_Accident)(MyEvent));
			if (TempAccident.IsAccident())//do not count twice (if Accident results are turning off)
			{
				TempNeighIndex = TempAccident.GetNeighborhood();//returns starting neighborhood
				TempRouteIndex = TempAccident.GetRoad();//DTG, does this use the 0-4 road indexing convention?
				myStatistics.SetAccidents(TempNeighIndex, TempRouteIndex);
			}
		}
		else if (MyEvent.GetEffect() == 2)//Car_Arrival
		{
			Car_Arrival TempArrival = ((Car_Arrival)(MyEvent));
			TempNeighIndex = TempArrival.GetOriginNeighborhood();
			TempRouteIndex = TempArrival.GetOriginRoad();
			TravelingTime = TempArrival.GetTime() - TempArrival.GetCar().GetTotalTime();
			int j = TempArrival.GetCar().GetTotalTime();
			myStatistics.SetStats(TempNeighIndex, TempRouteIndex, TravelingTime, true);
		}
		else if (MyEvent.GetEffect() == 4)//Rain
		{
			myStatistics.SetRain();
		}
		else if(MyEvent.GetEffect() == 5)//Train_Arrival
		{
			Train_Arrival TempArrival = ((Train_Arrival)(MyEvent));
			if (TempArrival.GetTrain().IsTravelingCW())//if traveling Clockwise
			{
				TempNeighIndex = Neighborhood.Prev(TempArrival.GetDestination());//gets origin neighborhood
				TempRouteIndex = 5;//Clockwise track index
			}
			else//traveling Counter-Clockwise
			{
				TempNeighIndex = Neighborhood.Next(TempArrival.GetDestination());//gets origin neighborhood
				TempRouteIndex = 6;//Counter-Clockwise track index
			}
			
			TravelingTime = TempArrival.GetTime() - TempArrival.GetTrain().returnDepartTime();
			myStatistics.SetStats(TempNeighIndex, TempRouteIndex, TravelingTime, false);
		}
	}	
	
	/**
	 * GetStatAvgSpeedPerRoute returns the AverageSpeed of the route.
	 * 
	 * @param NeighIndex 
	 * 			Integer (0-3) that represents the origin neighborhood.
	 * @param RouteIndex 
	 * 			Integer (0-6) that represents the route of the origin neighborhood.
	 * @return This function returns the AverageSpeed (TotalSpeed/TotalCars) for
	 * 			a specific route.
	 */
	public double GetStatAvgSpeedPerRoute(int NeighIndex, int RouteIndex) 
	{	return myStatistics.GetAverageSpeedPerRoute(NeighIndex, RouteIndex);	}
	
	
	/**
	 * GetStatAverageTravelTimePerRoute returns the AverageSpeed of the route.
	 * 
	 * @param NeighIndex 
	 * 			Integer (0-3) that represents the origin neighborhood.
	 * @param RouteIndex 
	 * 			Integer (0-6) that represents the route of the origin neighborhood.
	 * @return This function returns the AverageSpeed (TotalSpeed/TotalCars) for
	 * 			a specific route.
	 */
	public double GetStatAverageTravelTimePerRoute(int NeighIndex, int RouteIndex) 
	{	return myStatistics.GetAverageTravelTimePerRoute(NeighIndex, RouteIndex);	}
	
	/**
	 * GetStatVehiclesPerRoute returns the total number of vehicles that traveled a route.
	 * 
	 * @param NeighIndex
	 * 			Integer (0-3) that represents the origin neighborhood.
	 * @param RouteIndex 
	 * 			Integer (0-6) that represents the route of the origin neighborhood.
	 * @return This function returns TotalVehicles.
	 */
	public int GetStatVehiclesPerRoute(int NeighIndex, int RouteIndex) 
	{	return myStatistics.GetVehiclesPerRoute(NeighIndex, RouteIndex);	}
	
	/**
	 * GetStatNumAccidentsPerRoad returns the number of accidents that occured.
	 * 
	 * @param NeighIndex
	 * 			Integer (0-3) that represents the origin neighborhood.
	 * @param RoadIndex
	 * 			Integer (0-4) that represents the road of the origin neighborhood.
	 * @return This function returns NumAccidents.
	 */
	public int GetStatNumAccidentsPerRoad(int NeighIndex, int RoadIndex) 
	{	return myStatistics.GetNumAccidentsPerRoad(NeighIndex, RoadIndex);	}
	
	/**
	 * GetStatTotalNumAccidents returns the toal number of accidents that occured in the simulation.
	 * 
	 * @return This function returns the sum of all accidents.
	 */
	public int GetStatTotalNumAccidents() 
	{	return myStatistics.GetTotalNumAccidents();	}

	/**
	 * GetStatRainHistory returns whether it rained or not.
	 * 
	 * @return This function returns isRain.
	 */
	public boolean GetStatRainHistory() 
	{	return myStatistics.GetRainHistory();	}	
	
	/**
	 * GetStatAvgVehiclesPerHour returns the average number of cars/trains 
	 * that traversed a certain route per hour.
	 * 
	 * @param NeighIndex
	 * 			Integer (0-3) that represents the origin neighborhood.
	 * @param RouteIndex 
	 * 			Integer (0-6) that represents the route of the origin neighborhood.
	 * 
	 * @return This function returns Vehicles/Hour/Route.
	 */
	public double GetStatAvgVehiclesPerHour(int NeighIndex, int RouteIndex) 
	{	return myStatistics.GetAvgVehiclesPerHour(NeighIndex, RouteIndex, CurrentTime);	}
	

}